/* 
 * $Id$
 * 
 * Copyright (c) 2007-13 Stephane GALLAND.
 * 
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 * 
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 * 
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 * This program is free software; you can redistribute it and/or modify
 */
package fr.utbm.gi.vi51.project.environment;

import java.util.ArrayList;
import java.util.Collection;
import java.util.ConcurrentModificationException;
import java.util.List;

import javax.vecmath.Vector2d;

import fr.utbm.gi.vi51.framework.environment.AbstractEnvironment;
import fr.utbm.gi.vi51.framework.environment.AgentBody;
import fr.utbm.gi.vi51.framework.environment.EnvironmentEvent;
import fr.utbm.gi.vi51.framework.environment.MotionInfluence;
import fr.utbm.gi.vi51.framework.environment.Perception;
import fr.utbm.gi.vi51.framework.environment.SituatedObject;
import fr.utbm.gi.vi51.framework.time.SimulationTimeManager;
import fr.utbm.gi.vi51.project.agent.Marine;
import fr.utbm.gi.vi51.project.agent.body.LeaderBody;
import fr.utbm.gi.vi51.project.agent.body.ProjectileBody;
import fr.utbm.gi.vi51.project.agent.body.UnitBody;

/**
 * Model of the world.
 * 
 * @author St&eacute;phane GALLAND &lt;stephane.galland@utbm.fr&gt;
 * @version $Name$ $Revision$ $Date$
 */
public class WorldModel extends AbstractEnvironment implements WorldModelStateProvider {

	private static ArrayList<Marine> marines;
	private static EnvironmentObject target;

	/**
	 * @param width is the width of the world.
	 * @param height is the height of the world.
	 */
	public WorldModel(double width, double height) {
		super(width, height, new SimulationTimeManager(500));
		marines = new ArrayList<>();
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public WorldModelState getState() {
		Collection<SituatedObject> objects2 = new ArrayList<SituatedObject>(cloneAgentBodies());
		Collection<SituatedObject> objects = new ArrayList<SituatedObject>(cloneEnvironmentObjet());
		objects.addAll(objects2);

		return new WorldModelState(objects);
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	protected EnvironmentEvent createEnvironmentEvent() {
		return new WorldModelEvent(this);
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	protected List<Perception> computePerceptionsFor(AgentBody agent) {
		List<Perception> perceptionList = new ArrayList<>();
		try {
			if (!(agent instanceof ProjectileBody)) {
				for (AgentBody agentBody : getAgentBodies()) {
					if (((UnitBody) agent).getFrustrum().isVisible(agent.getPosition(), agentBody.getPosition())) {
						perceptionList.add(new Perception(agentBody));
					}
				}

				for (EnvironmentObject eo : getEnvironementObjet()) {
					if (eo.getType() == EnvironmentType.TARGET) {
						if (agent instanceof LeaderBody) {
							perceptionList.add(new Perception(eo));
						}
					} else if (((UnitBody) agent).getFrustrum().isVisible(agent.getPosition(), eo.getPosition())) {
						perceptionList.add(new Perception(eo));
					}
				}
			} else {
				for (AgentBody agentBody : getAgentBodies()) {
					if (((ProjectileBody) agent).getFrustrum().isVisible(agent.getPosition(), agentBody.getPosition())) {
						perceptionList.add(new Perception(agentBody));
					}
				}
			}
		} catch (ConcurrentModificationException e) {
			System.out.println("Caught ConcurrentModificationException"); //$NON-NLS-1$
		}

		return perceptionList;
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	protected void applyInfluences(Collection<MotionInfluence> influences, SimulationTimeManager timeManager) {
		for (MotionInfluence influence : influences) {
			Vector2d instantTranslation = computeKinematicTranslation(getAgentBodyFor(influence.getEmitter()),
					influence.getLinearInfluence(), timeManager);
			double instantRotation = computeKinematicRotation(getAgentBodyFor(influence.getEmitter()), influence
					.getAngularInfluence(), timeManager);
			move(getAgentBodyFor(influence.getEmitter()), instantTranslation, instantRotation);
		}
	}

	public static void addMarine(Marine marine) {
		WorldModel.marines.add(marine);
	}

	public static void removeMarine(Marine marine) {
		WorldModel.marines.remove(marine);
	}

	public static ArrayList<Marine> getMarines() {
		return marines;
	}

	public static EnvironmentObject getTarget() {
		return target;
	}

	public static void setTarget(EnvironmentObject target) {
		WorldModel.target = target;
	}

}